package net.qiujuer.lesson.sample.foo.command;

import net.qiujuer.lesson.sample.foo.ClientModel;
import net.qiujuer.lesson.sample.foo.ClientTypeEnum;
import net.qiujuer.lesson.sample.foo.CommandEnum;
import net.qiujuer.lesson.sample.foo.EnumUtils;

import java.nio.ByteBuffer;

public class CommandConstants {
    public static char BEGIN_CHAR = '\t';
    public static int COMMAND_SIZE = 1024;
    public static int STRING_SIZE = 40;

    private static String addNone(String content, int length) {
        if (content == null) {
            return null;
        }
        content = content.trim();
        if (content.length() >= length)
            return content;
        final int lengthContent = content.length();
        int size = length - lengthContent;
        int index = 0;
        while (index < size) {
            content += " ";
            index++;
        }
        return content;
    }

    public ClientModel getClientModel(byte[] bytes) throws NoSuchMethodException {
        if (bytes == null || bytes.length == 0)
            return null;

        ClientModel clientModel = new ClientModel();
        ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);

        if (byteBuffer.getChar() == BEGIN_CHAR) {
            int len = byteBuffer.getInt();
            System.out.println("总长度：" + len);
            if (len > 0) {
                final byte command = byteBuffer.get();
                final CommandEnum getValue = EnumUtils.valueOf(CommandEnum.class, command, CommandEnum.class.getMethod("getCommand"));
                boolean checkSign = true;
                if (getValue.isCheckSite()) {
                    final byte oddCheckSite = getOddCheckSite(bytes, bytes.length - 1);
                    if (oddCheckSite != bytes[bytes.length - 1])
                        checkSign = false;
                }

                if (checkSign) {
                    final byte clientType = byteBuffer.get();
                    clientModel.setClientType(clientType);
                    len -= 8;
                    byte[] bytes1 = new byte[STRING_SIZE];
                    byteBuffer.get(bytes1, 0, STRING_SIZE);
                    clientModel.setId(new String(bytes1).trim());
                }
            }
        }
        return clientModel;
    }

    /**
     * 发送心跳连接
     *
     * @return
     */
    public static byte[] getSEND_HEAD() {
        CommandEnum commandEnum = CommandEnum.SEND_HEAD;
        ByteBuffer byteBuffer = ByteBuffer.allocate(COMMAND_SIZE);
        System.out.println("初始化:" + byteBuffer.position());

        byteBuffer.putChar(BEGIN_CHAR);//加入开始符
        System.out.println("加入开始符:" + byteBuffer.position());

        byteBuffer.putInt(0);//加入长度
        System.out.println("加入长度:" + byteBuffer.position());

        byteBuffer.put(commandEnum.getCommand());//加入命令符
        System.out.println("加入命令符:" + byteBuffer.position());

        byteBuffer.putInt(2, byteBuffer.position());
        System.out.println("长度:" + byteBuffer.position());

        byteBuffer.flip();//反转
        int byteLen = byteBuffer.limit() - byteBuffer.position();
        if (commandEnum.isCheckSite())
            byteLen++;
        byte[] returnBytes = new byte[byteLen];

        if (commandEnum.isCheckSite()) {
            byteBuffer.get(returnBytes, 0, byteLen - 1);
            final byte oddCheckSite = getOddCheckSite(returnBytes, byteLen - 1);
            returnBytes[byteLen - 1] = oddCheckSite;
        } else
            byteBuffer.get(returnBytes, 0, byteLen);

        return returnBytes;

    }

    /**
     * 门禁机注册
     *
     * @param sn 门禁机sn
     * @return
     */
    public static byte[] getDOOR_IDENTITY_Command(String sn) {
        ClientTypeEnum clientTypeEnum = ClientTypeEnum.DOOR_APP;
        CommandEnum commandEnum = CommandEnum.DOOR_IDENTITY;
        sn = addNone(sn, STRING_SIZE);
        ByteBuffer byteBuffer = ByteBuffer.allocate(COMMAND_SIZE);
        System.out.println("初始化:" + byteBuffer.position());

        byteBuffer.putChar(BEGIN_CHAR);//加入开始符
        System.out.println("加入开始符:" + byteBuffer.position());

        byteBuffer.putInt(0);//加入长度
        System.out.println("加入长度:" + byteBuffer.position());

        byteBuffer.put(commandEnum.getCommand());//加入命令符
        System.out.println("加入命令符:" + byteBuffer.position());

        byteBuffer.put(clientTypeEnum.getClientType());//标识客户端类型
        System.out.println("标识客户端类型:" + byteBuffer.position());

        byteBuffer.put(sn.getBytes());//加入客户端标识
        System.out.println("加入客户端标识:" + byteBuffer.position());

        byteBuffer.putInt(2, byteBuffer.position());
        System.out.println("长度:" + byteBuffer.position());

        byteBuffer.flip();//反转
        int byteLen = byteBuffer.limit() - byteBuffer.position();
        if (commandEnum.isCheckSite())
            byteLen++;
        byte[] returnBytes = new byte[byteLen];

        if (commandEnum.isCheckSite()) {
            byteBuffer.get(returnBytes, 0, byteLen - 1);
            final byte oddCheckSite = getOddCheckSite(returnBytes, byteLen - 1);
            returnBytes[byteLen - 1] = oddCheckSite;
        } else
            byteBuffer.get(returnBytes, 0, byteLen);

        return returnBytes;
    }

    /**
     * 偶校验位算法
     *
     * @param bytes
     * @return
     */
    public static byte getEvenCheckSite(byte[] bytes, int checkLength) {
        return getCheckSite(bytes, false, checkLength);
    }

    /**
     * 奇校验位算法
     *
     * @param bytes
     * @return
     */
    public static byte getOddCheckSite(byte[] bytes, int checkLength) {
        return getCheckSite(bytes, true, checkLength);
    }

    private static byte getCheckSite(byte[] bytes, boolean oddSign, int checkLength) {
        int count = 0;
        for (int i = 0; i < checkLength; i++) {
            count += getOneCount(bytes[i]);
        }
        if (oddSign)
            return (byte) (count % 2 == 1 ? 0 : 1);
        else
            return (byte) (count % 2 == 1 ? 1 : 0);
    }

    private static int getOneCount(byte v) {
        int count = 0;
        while (v != 0 && v != -1) {
            count += v & 0x01; //将v与00000001相与如果v的最后一位是1相与的结果是1，反之则是0。
            v >>= 1;    //右移一位。
//            v &= (byte) (v-1);   //v=v&(v-1)这个操作可以直接消除掉v中的最右边的1。
//            count++;
        }
        return count;
    }

}
